package action

/*************************************************
 *	@author zander
 *	@time 	2018年1月2日14:12:37
 *	@brief 	所有返回数据控制器
 *************************************************/

import (
	"github.com/gin-gonic/gin"
	"net/http"
	"gopkg.in/mgo.v2/bson"
	"reflect"
	"hub.hddznet.com/uniplatform/framework/logging"
	"hub.hddznet.com/app/framework/errs"
	"hub.hddznet.com/app/framework/tools"
	"hub.hddznet.com/app/framework/app"
)

/*
	不管返回正确或者返回错误，都可以交给他来执行
 	ctx:gin上下文
	param:需要注入的参数对象
	checkType:参数验证的方式 [0:不做任何验证 1:若tag中包含ck则进行数据非空验证 2:tag中包含uck的属性不会进行非空验证，其他都会验证]

	可以直接返回{"Code":0,"Message":"OK","Response":{}}格式，程序将不会对返回结果做任何报装处理
*/

func Do(ctx *gin.Context, param interface{}, checkType int, fn func() (interface{}, error)) {
	//1. 读取请求中的数据流
	data := app.GinBodyData(ctx)
	if param != nil && len(data) != 0 {
		if reflect.TypeOf(param).Kind() != reflect.Ptr || reflect.TypeOf(param).Elem().Kind() != reflect.Struct {
			panic("参数提必须为指针类型，指向一个struct")
		}
		//3. 读取请求中的数据
		err := ctx.BindJSON(param)
		if err != nil {
			Fail(ctx, errs.ParamDataNilErr())
			return
		}
		err = paramCheck(param, checkType)
		if err != nil {
			Fail(ctx, err)
			return
		}
	}

	obj, err := fn()
	if err != nil {
		Fail(ctx, err)
		return
	}
	Success(ctx, obj)
	return
}

//进行参数检查，判断tag中的ck属性
//1. 只能验证单层的struct对象，也就是说对象中不能再包含struct对象
func paramCheck(obj interface{}, checkType int) error {
	if checkType == 0 {
		return nil
	}

	switch checkType {
	case 1:
		return tools.RftStruct(obj, func(ty reflect.Type, vl reflect.Value, tag reflect.StructTag) error {
			// 包含ck的，需要进行非空验证
			if _, ok := tag.Lookup("ck"); !ok {
				return nil
			}
			switch ty.Kind() {
			case reflect.Ptr:
				if vl.Pointer() == 0 {
					return errs.ParamFieldNilErr(obj)
				}
			case reflect.String:
				if vl.String() == "" {
					return errs.ParamFieldNilErr(obj)
				}
			}
			return nil
		})
	case 2:
		return tools.RftStruct(obj, func(ty reflect.Type, vl reflect.Value, tag reflect.StructTag) error {
			// 包含uck的，不进行非空验证
			if _, ok := tag.Lookup("uck"); ok {
				return nil
			}
			switch ty.Kind() {
			case reflect.Ptr:
				if vl.Pointer() == 0 {
					return errs.ParamFieldNilErr(obj)
				}
			case reflect.String:
				if vl.String() == "" {
					return errs.ParamFieldNilErr(obj)
				}
			}
			return nil
		})
	}
	return nil
}

//请求成功后返回
func Success(ctx *gin.Context, obj ... interface{}) {
	switch len(obj) {
	case 0:
		result(ctx, 0, "ok", "success")
	case 1:
		if obj[0] == nil {
			result(ctx, 0, "ok", nil)
			return
		}
		if tools.RftRealKind(reflect.TypeOf(obj[0])) == reflect.Slice {
			result(ctx, 0, "ok", obj[0])
			return
		}
		r, err := tools.TsnObjToMap(obj[0])
		if err != nil {
			Fail(ctx, err)
			return
		}
		var okcode, okmessage, okres = false, false, false
		_, okcode = r["Code"]
		_, okmessage = r["Message"]
		_, okres = r["Response"]
		if okcode && okmessage && okres {
			oresult(ctx, obj[0])
		} else {
			result(ctx, 0, "ok", obj[0])
		}
	default:
		result(ctx, 0, "ok", obj)
	}
}

//请求失败后返回
func Fail(ctx *gin.Context, err interface{}) {
	r, err := tools.TsnObjToMap(err)
	code, okcode := r["Code"]
	message, okmessage := r["Message"]
	res, okres := r["Response"]
	if okcode && okmessage && okres {
		if res != nil {
			ty := reflect.TypeOf(res)
			vl := reflect.ValueOf(res)
			if ty.Kind() == reflect.Slice {
				if vl.Len() == 1 {
					result(ctx, code, message, vl.Index(0).Interface())
					return
				}
			}
		}
		result(ctx, code, message, res)
	} else {
		logging.Error("异常未识别错误", err)
		result(ctx, 99999, "异常未识别错误", err)
	}
}
func result(c *gin.Context, code, msg, res interface{}) {
	r := bson.M{
		"Code":     code,
		"Message":  msg,
		"Response": res,
	}

	oresult(c, r)
}

// 在返回最终结果之前
var beforeReturnData = make([]func(c *gin.Context, res interface{}), 0)

func BeforeReturnData(fn func(c *gin.Context, res interface{})) {
	beforeReturnData = append(beforeReturnData, fn)
}

// 记录最终结果日志
func oresult(c *gin.Context, res interface{}) {
	for _, fn := range beforeReturnData {
		fn(c, res)
	}

	c.JSON(http.StatusOK, res)
	c.Status(http.StatusOK)
}
